#include <linux/module.h>
#include <linux/fs.h>
#include <linux/cdev.h>
#include <linux/io.h>
#include <linux/errno.h>
#include <linux/uaccess.h>
#include <linux/platform_device.h>
#include <linux/device.h>
#include <linux/of.h>
#include <linux/pinctrl/consumer.h>
#include <linux/pinctrl/machine.h>
#include <linux/pinctrl/devinfo.h>
#include <linux/pinctrl/pinctrl.h>
#include <linux/list.h>
#include <linux/printk.h>
#include <linux/interrupt.h>
#include <linux/of_irq.h>
#include <linux/of_gpio.h>
#include <linux/gpio/consumer.h>
#include <linux/timer.h>
#include <linux/delay.h>

#define EINT_KEY_IRQ_CNT 4

struct eint_key_irq_info {
    int irq;
    void *dev_id;
    struct work_struct eint_keys_work;
};

static struct eint_key_irq_info info_irq;

static irqreturn_t eint_keys(int irq, void *dev_id)
{
//    printk("%s enter, irq: %d, %s\n", __func__, irq, (char *)dev_id);
    info_irq.irq = irq;
    info_irq.dev_id = dev_id;

    /*调度 工作队列去执行,注意,这里只是设置一个标志位,并不是立马执行
        内核在合适的时机,去掉用工作队列,内核线程会依次执行每个工作队列的内容,
        可以睡眠,在进程上下文中处理,本质是内核工作队列线程
    */

    // 添加入队列的工作完成后会自动从队列中删除,只会执行1次
    schedule_work(&info_irq.eint_keys_work);
    
    return IRQ_HANDLED;
}

static void eint_keys_work_func(struct work_struct *data)
{
    // 使用container_of获取data中的其提的数据
    struct eint_key_irq_info *info = container_of(data, struct eint_key_irq_info, eint_keys_work);
    msleep(1000);
    printk("%s enter, irq: %d, %s\n", __func__, info->irq, (char *)info->dev_id);
    
}

/**
 * 当驱动和硬件信息匹配成功之后，就会调用probe函数，驱动所有的资源的注册和初始化全部放在probe函数中
 */
static int eint_key_probe(struct platform_device *pdev)
{
    struct device_node *np = NULL;
    int ret = 0;
    int i = 0;
    char *key_name = NULL;
    
    np = pdev->dev.of_node;
    // 通过platform_device获取设备节点
    if (np == NULL) {
        dev_err(&pdev->dev, "eint_key_probe failed");
        return -EINVAL;
    }

    // 通过irq_of_parse_and_map节点获取中断号
    for (i = 0; i < EINT_KEY_IRQ_CNT; i++) {
        // 获取中断资源
        ret = irq_of_parse_and_map(np, i);
        if (ret <= 0) {
            dev_err(&pdev->dev, "cannot find irq index %d\n", i);
            return ret;
        }

        key_name = kasprintf(GFP_KERNEL, "key-%d", i+1);
        // 注册中断号
        ret = devm_request_irq(&pdev->dev, ret, eint_keys, 0, key_name, key_name);
        if (ret != 0) {
            dev_err(&pdev->dev, "cannot claim irq %d\n", ret);
            return ret;
        }
    }

    // 初始化内核中共享的work队列,在进程上下文处理,内核线程中依次调用,可以睡眠
    INIT_WORK(&info_irq.eint_keys_work, eint_keys_work_func);
    

    // 创建私有的工作队列参考 https://zhuanlan.zhihu.com/p/469007029

    return 0;
}

/**
 * 硬件信息被移除了，或者驱动被卸载了，全部要释放，释放资源的操作就放在该函数中
 */
static int eint_key_remove(struct platform_device *pdev)
{
    return 0;
}

/**
 * 用于设备树匹配
 */
static const struct of_device_id eint_key_dt_match[] = {
    { .compatible = "eint-keys", },
    {},
};

/**
 * platform驱动
 */
static struct platform_driver eint_key_driver = {
    .probe = eint_key_probe,
    .remove = eint_key_remove,
    .driver = {
        .name = "eint_key",
        .of_match_table = eint_key_dt_match,  // 匹配列表
    }
};


static int eint_key_init(void)
{
    int ret = platform_driver_register(&eint_key_driver);
    if (ret < 0) {
        pr_err("eint_key_init platform_driver_register failed");
        return -EINVAL;
    }

    return 0;
}

/**
 * platform驱动模块出口
 */
static void eint_key_exit(void)
{
    platform_driver_unregister(&eint_key_driver);
}

module_init(eint_key_init);
module_exit(eint_key_exit);

MODULE_AUTHOR("huang");
MODULE_DESCRIPTION("jz2440 eint key driver");
MODULE_LICENSE("GPL");



